﻿using ServiceStack.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackExchange.Redis
{
    public static class TypedSetExtensionMethods
    {
        public static TItem[] HashGet<TItem>(this IDatabase database, RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
        {
            var values = database.HashGet(key, hashFields, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];
            var items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
            }

            return items;
        }

        public static bool SetAdd<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetAdd(key, value, flags);
        }

        public static Task<bool> SetAddAsync<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetAddAsync(key, value, flags);
        }

        public static long SetAdd<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return 0;
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.SetAdd(key, values, flags);
        }

        public static Task<long> SetAddAsync<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return new Task<long>(() => 0);
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.SetAddAsync(key, values, flags);
        }

        public static TItem[] SetCombine<TItem>(this IDatabase database, SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
        {
            var values = database.SetCombine(operation, keys, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];
            TItem[] items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
            }
            return items;
        }

        public static Task<TItem[]> SetCombineAsync<TItem>(this IDatabase database, SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
        {
            return database.SetCombineAsync(operation, keys, flags).ContinueWith(task =>
            {
                var values = task.Result;
                if (values == null) return null;
                if (values.Length == 0) return new TItem[0];
                TItem[] items = new TItem[values.Length];
                for (int i = 0; i < values.Length; i++)
                {
                    items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
                }
                return items;
            });
        }


        public static TItem[] SetCombine<TItem>(this IDatabase database, SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
        {
            var values = database.SetCombine(operation, first, second, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];
            TItem[] items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
            }
            return items;
        }

        public static Task<TItem[]> SetCombineAsync<TItem>(this IDatabase database, SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
        {
            return database.SetCombineAsync(operation, first, second, flags).ContinueWith(task =>
            {
                var values = task.Result;
                if (values == null) return null;
                if (values.Length == 0) return new TItem[0];
                TItem[] items = new TItem[values.Length];
                for (int i = 0; i < values.Length; i++)
                {
                    items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
                }
                return items;
            });
        }

        public static bool SetContains<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetContains(key, value, flags);
        }

        public static Task<bool> SetContainsAsync<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetContainsAsync(key, value, flags);
        }

        public static TItem[] SetMembers<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            var values = database.SetMembers(key, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];
            TItem[] items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
            }
            return items;
        }

        public static Task<TItem[]> SetMembersAsync<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return database.SetMembersAsync(key, flags).ContinueWith(task =>
            {
                var values = task.Result;
                if (values == null) return null;
                if (values.Length == 0) return new TItem[0];
                TItem[] items = new TItem[values.Length];
                for (int i = 0; i < values.Length; i++)
                {
                    items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
                }
                return items;
            });

        }

        public static bool SetMove<TItem>(this IDatabase database, RedisKey source, RedisKey destination, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetMove(source, destination, value, flags);
        }

        public static Task<bool> SetMoveAsync<TItem>(this IDatabase database, RedisKey source, RedisKey destination, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.SetMoveAsync(source, destination, value, flags);
        }

        public static TItem SetPop<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            var value = database.SetPop(key, flags);
            if (value.HasValue)
            {
                return JsonSerializer.DeserializeFromString<TItem>(value);
            }
            else
            {
                return default(TItem);
            }
        }

        public static Task<TItem> SetPopAsync<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return database.SetPopAsync(key, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    return JsonSerializer.DeserializeFromString<TItem>(value);
                }
                else
                {
                    return default(TItem);
                }
            });
        }

        public static TItem SetRandomMember<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            var value = database.SetRandomMember(key, flags);
            if (value.HasValue)
            {
                return JsonSerializer.DeserializeFromString<TItem>(value);
            }
            else
            {
                return default(TItem);
            }
        }

        public static Task<TItem> SetRandomMemberAsync<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return database.SetRandomMemberAsync(key, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    return JsonSerializer.DeserializeFromString<TItem>(value);
                }
                else
                {
                    return default(TItem);
                }
            });
        }


        public static TItem[] SetRandomMembers<TItem>(this IDatabase database, RedisKey key, long count, CommandFlags flags = CommandFlags.None)
        {
            var values = database.SetRandomMembers(key, count, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];
            TItem[] items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
            }
            return items;
        }

        public static Task<TItem[]> SetRandomMembersAsync<TItem>(this IDatabase database, RedisKey key, long count, CommandFlags flags = CommandFlags.None)
        {
            return database.SetRandomMembersAsync(key, count, flags).ContinueWith(task =>
            {
                var values = task.Result;
                if (values == null) return null;
                if (values.Length == 0) return new TItem[0];
                TItem[] items = new TItem[values.Length];
                for (int i = 0; i < values.Length; i++)
                {
                    items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i]);
                }
                return items;
            });
        }


        public static long SetRemove<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return 0;
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.SetRemove(key, values, flags);
        }

        public static Task<long> SetRemoveAsync<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return new Task<long>(() => 0);
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.SetRemoveAsync(key, values, flags);
        }

        public static bool SetRemove<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            if (item == null) return false;
            var value = JsonSerializer.SerializeToString(item);
            return database.SetRemove(key, value, flags);
        }
        public static Task<bool> SetRemoveAsync<TItem>(this IDatabase database, RedisKey key, TItem item, CommandFlags flags = CommandFlags.None)
        {
            if (item == null) return new Task<bool>(() => false);
            var value = JsonSerializer.SerializeToString(item);
            return database.SetRemoveAsync(key, value, flags);
        }

    }
}
